package uk.ac.glasgow.demosocs.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import uk.ac.glasgow.demosocs.Candidate;
import uk.ac.glasgow.demosocs.Election;
import uk.ac.glasgow.demosocs.ElectionState;
import uk.ac.glasgow.demosocs.OneOfNVote;
import uk.ac.glasgow.demosocs.Vote;
import uk.ac.glasgow.demosocs.VotingSystem;
import uk.ac.glasgow.demosocs.users.*;
import uk.ac.glasgow.demosocs.electionstore.*;
import uk.ac.glasgow.demosocs.electoratestore.*;

/**
 * An implementation of a voting system.
 * @author Team K
 */
public class VotingSystemImpl implements VotingSystem {
	private User currentUser;
	private ElectionImpl currentElection;
	private ElectorateStoreImpl electorate = new ElectorateStoreImpl("electoratestore.txt");
	private UserStoreImpl userStore = new UserStoreImpl("userstore.txt");
	private ElectionStoreImpl eStore = new ElectionStoreImpl("electionstore.txt");

	/**
	 * Accepts a nomination as that nominee.
	 */
	@Override
	public void acceptNomination() {
		CandidateImpl c = new CandidateImpl(currentUser.getGUID());
		currentElection.addCandidate(c);
		currentElection.addNomination(currentUser);	
		currentUser.removeNomination(currentElection);
		userStore.storeUser(currentUser.getGUID(),currentUser);
		eStore.storeElection(currentElection.getEID(), currentElection);
	}

	/**
	 * Casts a vote with the given details.
	 * @param vote a vote object detailing the vote information
	 */
	@Override
	public void castVote(Vote vote) {
		OneOfNVoteStub v = (OneOfNVoteStub) vote;
		if (electorate.isEligible(currentElection.getElectorateID(), currentUser.getGUID())){
			currentElection.addVote(v.getCandidateGUID());
			eStore.storeElection(currentElection.getEID(), currentElection);
		}
	}

	/**
	 * Creates an election with the supplied parameters
	 * @param name the name of the election
	 * @param typeID the type ID of the election
	 */
	@Override
	public void createElection(String name, String typeID) {
		if(currentUser.getIsManager()) {
			ElectionStore electionStore = new ElectionStoreImpl("electionstore.txt");
			electionStore.addElection(name, typeID);
			currentElection = (ElectionImpl) electionStore.getElection(name);
		}
	}

	/**
	 * Edits the current election to match the supplied details.
	 * @param openNominations the date to start nominations
	 * @param start the date to open the election
	 * @param end the date to close the election
	 * @param electoratelectionID the electorate grouping the election is to be for
	 */
	@Override
	public void editElection(Date openNominations, Date start, Date end,
			String electoratelectionID) {
		if(currentUser.getIsManager()) {
			ElectionStore electionStore = new ElectionStoreImpl("electionstore.txt");
			electionStore.editElection(currentElection.getEID(), openNominations, start, end, electoratelectionID);
		}
		electorate.addUserToElectorate(electoratelectionID, "0901634");
		electorate.addUserToElectorate(electoratelectionID, "0901635");
	}

	/**
	 * Gets a set of all elections
	 * @return a set of all elections
	 */
	@Override
	public Set<Election> getElections() {
		ElectionStore electionStore = new ElectionStoreImpl("electionstore.txt");
		return electionStore.getElections();
	}

	/**
	 * Gets all nominations for the current user
	 * @return a set of all elections for which the user is nominated
	 */
	@Override
	public Set<Election> getNominations() {
		return currentUser.getNominations();
	}

	/**
	 * Nominates the supplied candidate for a place in the current election
	 * @param GUID the user to be nominated
	 */
	@Override
	public void nominateCandidate(String GUID) {
		if (currentElection.getElectionState() == ElectionState.NOMINATIONS_OPEN && electorate.isEligible(currentElection.getElectorateID(),GUID) && electorate.isEligible(currentElection.getElectorateID(), currentUser.getGUID())){
			if (currentUser.getGUID().equals(GUID)){
				currentUser.addNomination(currentElection);
			}
			else {
				User user = userStore.getUserWithGUID(GUID);
				user.addNomination(currentElection);
				userStore.storeUser(user.getGUID(),user);
			}
		}

	}

	/**
	 * Publishes the selected election's results.
	 */
	@Override
	public void publishResults() {
		// TODO Auto-generated method stub
		if (currentElection != null &&
				currentElection.getElectionState() == ElectionState.ENDED) {
			HashMap<String, Integer> votes = currentElection.getVotes();
			String winnerID = null;
			int winnerVotes = 0;
			
			for ( Map.Entry<String, Integer> v : votes.entrySet() ) {
				if (v.getValue() > winnerVotes) {	
					winnerVotes = v.getValue();	
					winnerID = v.getKey();
					System.out.println(winnerID);
				}
			}
			
			ArrayList<Candidate> winners = new ArrayList<Candidate>();
			Candidate w = new CandidateImpl(winnerID);
			winners.add(w);
			ElectionResultImpl er = new ElectionResultImpl(winners);
			currentElection.setElectionResult(er);
			eStore.storeElection(currentElection.getEID(),currentElection);
		}
		
			
	}

	/**
	 * Sets the current election to the one which had the supplied ID
	 * @param electionID the election to switch to
	 */
	@Override
	public void setCurrentElection(String electionID) {
		ElectionStore electionStore = new ElectionStoreImpl("electionstore.txt");
		currentElection = (ElectionImpl) electionStore.getElection(electionID);
	}

	/**
	 * Tries to login with the provided details
	 * @param GUID the guid to login with
	 * @param password the password that matches the account
	 * @return true if the user has been logged in, false otherwise
	 */
	@Override
	public boolean setCurrentUser(String GUID, String password) {
		UserStore usrStore = new UserStoreImpl("userstore.txt");
		usrStore.addUser("McBobster", "0901634", "Bob", "changeme",true);
		usrStore.addUser("McBobster Jnr", "0901635", "Bob", "changeme",false);
		usrStore.addUser("McBobster Snr", "0901636", "Bob", "changeme",false);
		
		//added by tester
		usrStore.addUser("b", "1", "a", "p", false);
		usrStore.addUser("d", "2", "c", "p", false);
		usrStore.addUser("f", "3", "e", "p", true);

		electorate.addElectorate("level3");
		electorate.addElectorate("levelAWESOME");
		electorate.addUserToElectorate("level3", "0901635");
		electorate.addUserToElectorate("levelAWESOME", "0901634");
		electorate.addUserToElectorate("levelAWESOME", "0901635");
		electorate.addUserToElectorate("levelAWESOME", "0901636");
		electorate.addUserToElectorate("level3", "1");
		electorate.addUserToElectorate("level3", "2");
		electorate.addUserToElectorate("level3", "3");

		currentUser = usrStore.getUser(GUID, password);
		return (currentUser != null);
	}

	/**
	 * Returns the current election
	 * @return the current election
	 */
	@Override
	public Election getCurrentElection() {
		return currentElection;
	}

	/**
	 * Returns the currently logged in user's GUID
	 * @return the user's GUID
	 */
	@Override
	public String getCurrentUserGUID() {
		return currentUser.getGUID();
	}
}
